// test.h
//
// Unit Test for debugging class

//Disable the warning regarding long template names
#ifdef WIN32
#pragma warning(disable:4786)
#pragma warning(disable:4503)
#endif


#include "unitTest.h"
#include "object.h"
#include "debugStream.h"
#include "debugging.h"

#include <iostream>
#include <fstream>

using namespace simple;

namespace simple_test{

    #define NODEBUG 1

    UTFUNC(debug)
    {
        setDescription ("Simple Debugging tests");

        debugstream.sink (DebugSinkConsole::sOnly);
        DebugSinkConsole::sOnly.showHeader (true);

        cdebug << "This is output to the debug string" << std::endl;
        cdebug << "This is line 2";

        DebugSinkFile::sOnly.setFile ("test.txt");
        debugstream.sink (DebugSinkFile::sOnly);
        DebugSinkFile::sOnly.showHeader (true);
        cdebug << "This is output to the debug string" << std::endl;
        cdebug << "This is line 4";

        debugstream.sink (DebugSinkConsole::sOnly);

        int count = 0;
        Debug::gOnly().reset ();
        DEBUGGING(0, count++;)
        DEBUGGING(1, count++;)
        VERIFY (count == 0);

        count = 0;
        Debug::gOnly().debug (0, true);
        DEBUGGING(0, count++;)
        DEBUGGING(1, count++;)
        VERIFY (count == 1);

        count = 0;
        Debug::gOnly().debug (1, true);
        DEBUGGING(0, count++;)
        DEBUGGING(1, count++;)
        VERIFY (count == 2);

        count = 0;
        Debug::gOnly().debug (2, true);
        DEBUGGING(1, {
            count++;
            count++;
        })
        DEBUGGING(2, {
            count++;
            count++;
        })
        VERIFY (count == 4);


    }

    // This test only runs on win32 platforms
    #ifdef WIN32
    #include <windows.h>

    // Output our data to the windows debug console
    class DebugSinkWindows : public DebugSinkConsole
    {
    public:
        static DebugSinkWindows sOnly;
        virtual void display (const std::string& str);
    };

    DebugSinkWindows DebugSinkWindows::sOnly = DebugSinkWindows ();

    void DebugSinkWindows::display (const std::string& str)
    {
        OutputDebugString (str.c_str());
    }

    UTFUNC(windows)
    {
        setDescription ("OutputDebugString testing");

        debugstream.sink (DebugSinkWindows::sOnly);
        DebugSinkWindows::sOnly.showHeader (true);

        cdebug << "This is output to the debug string" << std::endl;
        cdebug << "This is line 2";

        VERIFY (true);
    }
    #endif

    UTFUNC(directlySink)
    {
        for (int i=0; i<10; i++)
            DebugSinkConsole::sOnly.write ('0' + i);
        DebugSinkConsole::sOnly.write ('\n');
    }

    // Not strictly a unittest, but show that I can redirect
    UTFUNC(streamredirect)
    {
        std::cout << "This should be written to the console" << std::endl;

        std::ofstream file ("redirect.txt");
        std::streambuf* oldbuf = std::cout.rdbuf (); // Save
        std::cout.rdbuf (file.rdbuf());

        std::cout << "This should be written to the file" << std::endl;

        std::cout.rdbuf (oldbuf); // Restore
        std::cout << "This should be written to the console" << std::endl;

    }

    UTFUNC(bookexample)
    {
    cdebug << "This line goes to our null sink" << std::endl;

    // Start like power-up
    DebugSinkConsole::sOnly.showHeader (false);
    DebugSinkFile::sOnly.showHeader (false);

    debugstream.sink (DebugSinkConsole::sOnly);
    cdebug << "This line goes to std::cout" << std::endl;

    DebugSinkConsole::sOnly.showHeader (true);
    cdebug << "Also to std::cout, but with a timestamp" << std::endl;

    DebugSinkFile::sOnly.setFile ("test.txt");
    debugstream.sink (DebugSinkFile::sOnly);
    cdebug << "This line goes to test.txt" << std::endl;

    DebugSinkFile::sOnly.showHeader (true);
    cdebug << "Also to test.txt, but with a timestamp" << std::endl;
    }

    UTFUNC(bookexample2)
    {
    DEBUGGING(1, cdebug << "This is level 1" << std::endl; );
    DEBUGGING(2, {
        for (int i=0; i<10; i++)
            cdebug << "This is line " << i << std::endl;
    });

    DEBUGGING(2,
        for (int i=0; i<10; i++)
            cdebug << "Again, this is line " << i << std::endl;
    );

    }

    UTFUNC(bookexample3)
    {
     debugstream.sink (DebugSinkConsole::sOnly);
     Debug::gOnly().reset ();

     cdebug << "set bit 0" << std::endl;
        Debug::gOnly().set (0);
    DEBUGGING(0, cdebug << "This is bit 0" << std::endl; );
    DEBUGGING(1, {
        for (int i=0; i<10; i++)
            cdebug << "bit 1This is line " << i << std::endl;
    });

        cdebug << "set bit 1" << std::endl;
        Debug::gOnly().clear (0);
        Debug::gOnly().set (1);
    DEBUGGING(0, cdebug << "This is bit 0" << std::endl; );
    DEBUGGING(1, {
        for (int i=0; i<10; i++)
            cdebug << "bit 1This is line " << i << std::endl;
    });
    }


    class Test : public Object<Test>
    {
    public:
        Test ()  {}
        ~Test () {}
        void test () {
            debugMessage ("Hello");
        }
        virtual std::string process (const std::string& command)
        {
            std::cout << "process: " << command.c_str() << std::endl;
            return command;
        }

    };


    UTFUNC(ctor)
    {
        setDescription ("Constructor and simple accessor tests");

        debugstream.sink (DebugSinkConsole::sOnly);

        Test test;
        test.test ();

        Test testb, testc;

        std::cout << "Map: " << ObjectMgr::gOnly().dump().c_str() << std::endl;

        // Try the list command
        std::string list = ObjectInfo<Test>::gOnly().process ("list");
        std::cout << "list command: " << list << std::endl;

        // Try the execute command
        std::string result = ObjectInfo<Test>::gOnly().process ("execute my command");
        std::cout << "execute command: " << result << std::endl;

        // Try the to command. We'll call the first instance
        Token parsed = StringTools::sParse (list);
        std::string cmd = "to " + parsed.parsed + " my command";
        result = ObjectInfo<Test>::gOnly().process (cmd);
        std::cout << "'" << cmd.c_str() << "' comand: " << result << std::endl;

        VERIFY (true);
    }


    #define CREATEINSTANCECLASS(classname)                            \
    class ObjectInfo_##classname : public ObjectInfoBase              \
        public:                                                       \
            static ObjectInfo_##classname gOnly ();                   \
            virtual std::string process (const std::string& command); \
            virtual std::string dump ();                              \
        private:                                                      \
            static ObjectInfo_##classname* _sOnly;                    \
            std::string _name;                                        \
            ObjectInfo_##classname ();                                \
        };
}

